package com.lee.hystrix.command;

import com.lee.hystrix.util.ReflectionUtil;
import com.netflix.hystrix.*;

import java.lang.reflect.Field;
import java.time.Instant;
import java.util.Random;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <br/>
 *
 * @author liyuanchang
 * @version V1.0
 * @email liyuanchang@chinamobile.com
 * @date 2019/6/18 9:37
 */
public class HelloCommand extends HystrixCommand<String> {
    private static final String THREAD_POOL_PREFIX = "HelloCommand-";

    private String groupKey;
    private int timeoutMilliseconds, sleepMillisecondsBound;
    private Random random = new Random();

    private StringBuilder result = new StringBuilder("call stack:\n");

    /**
     * 通过timeoutMilliseconds和sleepMillisecondsBound配合，可以控制失败的概率
     * @param groupKey 分组key，一个组一个独立的线程池
     * @param timeoutMilliseconds 正常方法执行的超时时间
     * @param sleepMillisecondsBound 用于生成一个[0, sleepMillisecondsBound]的数字，用于sleep，模拟业务逻辑时间
     */
    public HelloCommand(String groupKey, int timeoutMilliseconds, int sleepMillisecondsBound) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
                .andCommandKey(HystrixCommandKey.Factory.asKey(groupKey))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(THREAD_POOL_PREFIX + groupKey))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(timeoutMilliseconds))
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10)./*withMaxQueueSize(90).*/withQueueSizeRejectionThreshold(1000))
        );

        this.groupKey = groupKey;
        this.sleepMillisecondsBound = sleepMillisecondsBound;
        this.timeoutMilliseconds = timeoutMilliseconds;
    }

    public HelloCommand(Setter setter) {
        super(setter);
    }

    @Override
    protected String run() throws Exception {
        // 日志需要
        int time = random.nextInt(sleepMillisecondsBound);
        String str = getPrintString("call run(), sleep time = " + time + ", timeout = " + timeoutMilliseconds);
        result.append("\t").append(str).append("\n");
        System.out.println(str);

        // 模拟业务逻辑
        TimeUnit.MILLISECONDS.sleep(time);

        // 日志需要
        str = getPrintString("success!");
        result.append("return value : ").append(str).append("\n\n");
        return result.toString();
    }

    @Override
    protected String getFallback() {
        // 日志需要
        ThreadPoolExecutor threadPool = null;
        try {
            Field threadPoolField = ReflectionUtil.findField(HystrixCommand.class, "threadPool");
            threadPoolField.setAccessible(true);
            HystrixThreadPool hystrixThreadPool = (HystrixThreadPool) threadPoolField.get(this);
            Field field = ReflectionUtil.findField(HystrixThreadPool.HystrixThreadPoolDefault.class, "threadPool");
            field.setAccessible(true);
            threadPool = (ThreadPoolExecutor)field.get(hystrixThreadPool);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        int size = threadPool == null ? -1 : threadPool.getQueue().size();

        String str = getPrintString("call getFallback(), queue size = " + size);
        result.append("\t").append(str).append("\n");

        str = getPrintString(groupKey + " fallback!");
        System.out.println(str);
        result.append("return value : ").append(str).append("\n\n");
        return result.toString();
    }

    public static <T> void print(T msg) {
        System.out.println(getPrintString(msg));
    }

    public static <T> String getPrintString(T msg) {
        String name = Thread.currentThread().getName();
        return Instant.now() + " - " + name + " " + msg;
    }
}
